﻿using System;
using System.Collections.Generic;


namespace QSharp.Scheme.Utility
{
    using Classical.Trees;

    static class HBSpaceMgmtSystem
    {
        public interface INodePos
        {
            bool IsNull { get; }
            void Move(int n);
        }

        public interface IHBStream
        {
            int Encode(INodePos writeAt, byte[] buf, int start, int size);
            int Decode(INodePos readAt, byte[] buf, int start, int size);
        }

        public interface IEncodable
        {
            int Decode(IHBStream s, INodePos readAt);
            int Encode(IHBStream s, INodePos writeAt);
        }

        public class Hole<PosType, SizeType> : IComparable<Hole<PosType, SizeType>>
            where SizeType : IComparable<SizeType>
        {
            public PosType Start = default(PosType);
            public SizeType Size = default(SizeType);

            public int CompareTo(Hole<PosType, SizeType> other)
            {
                return this.Size.CompareTo(other.Size);
            }
        }

        public class HBNode<NodePos, Pos, Size> 
            : BTree.INode<HBNode<NodePos, Pos, Size>, Hole<Pos, Size>>, IDisposable
            where NodePos : IEncodable, INodePos, new()
            where Pos : IEncodable
            where Size : IEncodable, IComparable<Size>
        {
            public delegate void DisposedDelegate(HBNode<NodePos, Pos, Size> sender);

            public HBNode(IHBStream s, NodePos cp, int order, 
                int nodeEncSize, int entryEncSize, DisposedDelegate disposed)
            {
                HBStream = s;
                CurrentPos = cp;
                BTreeOrder = order;
                NodeEncSize = nodeEncSize;
                EntryEncSize = entryEncSize;
                Disposed = disposed;
            }

            public void Dispose()
            {
                if (Disposed != null)
                    Disposed(this);
            }

             /* INode implementation, beginning of */
            public HBNode<NodePos, Pos, Size> GetChild(int index)
            {
                NodePos np = Children[index];

                if (np.IsNull)
                    return null;

                HBNode<NodePos, Pos, Size> n = new HBNode<NodePos, Pos, Size>(
                    HBStream, np, BTreeOrder, NodeEncSize, EntryEncSize, Disposed);
                n.DecodeFromStream();

                return n;
            }

            public void SetChild(int index, HBNode<NodePos, Pos, Size> node)
            {
                Children[index] = node.CurrentPos;
            }

            public void AppendChild(HBNode<NodePos, Pos, Size> node)
            {
                Children.Add(node.CurrentPos);
            }

            public void InsertChild(int index, HBNode<NodePos, Pos, Size> node)
            {
                Children.Insert(index, node.CurrentPos);
            }

            public void RemoveChild(int index)
            {
                Children.RemoveAt(index);
            }

            public Hole<Pos, Size> GetEntry(int index)
            {
                return Entries[index];
            }

            public void SetEntry(int index, Hole<Pos, Size> entry)
            {
                Entries[index] = entry;
            }

            public void AppendEntry(Hole<Pos, Size> entry)
            {
                Entries.Add(entry);
            }

            public void InsertEntry(int index, Hole<Pos, Size> entry)
            {
                Entries.Insert(index, entry);
            }

            public void RemoveEntry(int index)
            {
                Entries.RemoveAt(index);
            }

            public void RemoveFrom(int start)
            {
                int c = Entries.Count - start;
                Entries.RemoveRange(start, c);
                Children.RemoveRange(start + 1, c);
            }

            public HBNode<NodePos, Pos, Size> Parent 
            {
                get
                {
                    if (ParentPos.IsNull)
                        return null;

                    HBNode<NodePos, Pos, Size> n = new HBNode<NodePos, Pos, Size>(
                        HBStream, ParentPos, BTreeOrder, NodeEncSize, EntryEncSize, Disposed);

                    n.DecodeFromStream();
                    return n;
                }
                set
                {
                    ParentPos = value.CurrentPos;
                }
            }

            public int EntryCount 
            { 
                get
                {
                    return Entries.Count;
                }
            }
            public int ChildCount 
            {
                get
                {
                    return Children.Count;
                }
            }

            public bool IsLeaf()
            {
                return Children[0].IsNull;
            }

            /* INode implementation, end of */

            /* Properites or Fields, beginning of */

            public IHBStream HBStream;

            public NodePos CurrentPos;

            public int BTreeOrder;

            public int NodeEncSize;
            public int EntryEncSize;

            public event DisposedDelegate Disposed;


            // data to be encoded
            public NodePos ParentPos;
            public List<Hole<Pos, Size>> Entries = new List<Hole<Pos,Size>>();
            public List<NodePos> Children = new List<NodePos>();


            /* Properites or Fields, end of */

            /* Serialization methods, beginning of */

            /**
             * <summary>
             *  Format of the stream encoded
             *   child_count
             *   ENTRY1 { holepos, holesize }
             *   ENTRY2 { holepos, holesize }
             *   ...
             *   ENTRYn-1 { holepos, holesize }
             *   ---------------------------
             *   PARENT
             *   CHILD1
             *   CHILD2
             *   ...
             *   CHILDn
             * 
             * </summary>
             */
            public virtual void EncodeToStream()
            {
                NodePos p = this.CurrentPos;

                int cc = Children.Count;
                byte[] bbuf = new byte[4];
                bbuf[0] = (byte)(cc >> 24);
                bbuf[1] = (byte)((cc >> 16) & 8);
                bbuf[2] = (byte)((cc >> 8) & 8);
                bbuf[3] = (byte)(cc & 8);

                int encSize = HBStream.Encode(p, bbuf, 0, 4);
                p.Move(encSize);

                int i;
                for (i = 0; i < Entries.Count; i++)
                {
                    Hole<Pos, Size> hole = Entries[i];
                    encSize = hole.Start.Encode(HBStream, p);
                    p.Move(encSize);
                    encSize = hole.Size.Encode(HBStream, p);
                    p.Move(encSize);
                }
                int maxc = BTree.MaximalEntryCount(BTreeOrder);
                p.Move(EntryEncSize * (maxc - i));

                ParentPos.Encode(HBStream, p);
                p.Move(NodeEncSize);
                for (i = 0; i < Children.Count; i++)
                {
                    Children[i].Encode(HBStream, p);
                    p.Move(NodeEncSize);
                }
            }

            public virtual void DecodeFromStream()
            {
                NodePos p = this.CurrentPos;
                byte[] bbuf = new byte[4];
                int decSize = HBStream.Decode(p, bbuf, 0, 4);
                p.Move(decSize);

                int cc = bbuf[0];
                cc <<= 8;
                cc |= bbuf[1];
                cc <<= 8;
                cc |= bbuf[2];
                cc <<= 8;
                cc |= bbuf[3];

                int i = 0;

                for (i = 0; i < cc - 1; i++)
                {
                    Hole<Pos, Size> hole = new Hole<Pos, Size>();
                    decSize = hole.Start.Decode(HBStream, p);
                    p.Move(decSize);
                    decSize = hole.Size.Decode(HBStream, p);
                    p.Move(decSize);
                    Entries.Add(hole);
                }
                int maxc = BTree.MaximalEntryCount(BTreeOrder);
                p.Move(EntryEncSize * (maxc - 1));

                ParentPos.Decode(HBStream, p);
                p.Move(NodeEncSize);

                for (i = 0; i < cc; i++)
                {
                    NodePos child = new NodePos();
                    child.Decode(HBStream, p);
                    Children.Add(child);
                    p.Move(NodeEncSize);
                }
            }

            /* Serialization methods, end of */
        }

        public class HBSpaceManager
        {
            public abstract class HBPos : INodePos
            {
                public abstract bool IsNull { get; }
                public abstract void Move(int n);
            }

            public interface IHBSize : IEncodable, IComparable<IHBSize>
            {
            }

            public abstract class HBNodeManager
                : BTree.INodeCreator<HBNode<HBPos, HBPos, IHBSize>, Hole<HBPos, IHBSize>>
            {
                public abstract HBNode<HBPos, HBPos, IHBSize> Create();
            }

            public class Section
            {
                public IHBStream Stream;
                public int ChunkSize;
                public int ChunkCount;

                public HBNodeManager NodeCreator;
            }

            public class Client
            {
                public IHBStream Stream;
                public int ChunkSize;
                public int ChunkCount;
            }

            public class RootSection
            {
                public List<Hole<IHBPos, IHBSize>> Holes;
            }

            protected RootSection mRootSection;
            protected Section[] mSections = null;
            protected Client mClient;

            void 




            public void Allocate(HBPos pos, IHBSize size)
            {
                throw new NotImplementedException();
            }

            public void Deallocate(HBPos pos)
            {
                throw new NotImplementedException();
            }

        }
    }
}
